﻿#define NEWPROPSGUMP
#define BOOKTEXTENTRY

using System;
using System.Data;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Network;
using Server.Gumps;
using Server.Targeting;
using System.Reflection;
using Server.Commands;
using CPA = Server.CommandPropertyAttribute;
using System.Xml;
using Server.Spells;
using System.Text;

/*
** Changelog
**
** 8/15/04
** - fixed a crash bug when using the goto spawn button on an empty spawn entry
**
** 8/10/04
** - added a goto-spawn button in the spawner gump (to the right of the text entry area, next to the text entry gump button) that will take you to the location of
** currently spawned objects for a given spawner entry.  If there are multiple spawned objects for an entry, it will cycle through them with repeated clicks.
** Useful for tracking down spawns.
** 3/23/04
** changed spawner name font color for 3dclient compatibility
*/

namespace Server.Mobiles
{
	public class HelpGump : Gump
	{
		public XmlSpawner m_Spawner;
		private XmlSpawnerGump m_SpawnerGump;

		public HelpGump(XmlSpawner spawner, XmlSpawnerGump spawnergump, int X, int Y)
			: base(X, Y)
		{
			if (spawner == null || spawner.Deleted)
				return;
			m_Spawner = spawner;
			m_SpawnerGump = spawnergump;

			AddPage(0);

            int width = 370;

			AddBackground(20, 0, width, 480, 5054);

			AddPage(1);
			//AddAlphaRegion( 20, 0, 220, 554 );
			AddImageTiled(20, 0, width, 480, 0x52);
			//AddImageTiled( 24, 6, 213, 261, 0xBBC );

			AddLabel(27, 2, 0x384, "Standalone Keywords NB: ( « stands for < ) ( » stands for > )");
			AddHtml(25, 20, width - 10, 440,
				"mobtype[,arg1,arg2,...]\n" +
				"SET[,itemname o seriale][,itemtype]/prop/value/...\n" +
				"SETONMOB,nomemob[,mobtype]/prop/value/...\n" +
				"SETONTHIS[,proptest]/prop/value/...\n" +
				"SETONTRIGMOB/prop/value/...\n" +
				"FOREACH,objecttype[,name][,distance]/action (KILL,DAMAGE,etc)\n" +
				"SETVAR,namevar/value\n" +
				"SETONNEARBY,distance,name[,type][,searchcontainers (true/false)]/prop/value/...\n" +
				"SETONPETS,distance[,name]/prop/value/...\n" +
				"SETONCARRIED,itemname[,itemtype][,equippedonly (true/false)]/prop/value/...\n" +
				"SETONSPAWN[,spawnername],subgroup/prop/value/...\n" +
				"SETONSPAWNENTRY[,spawnername],entrystring/prop/value/...\n" +
				"SETONPARENT/prop/value/...\n" +
				"TAKEGIVE[,quantity[,searchinbank (true/false),[type]]]/itemnametotake/GIVE/itemtypetogive\n" +
				"GIVE[,probability (0.01=1% 1=100%)]/itemtypetogive\n" +
				"GIVE[,probability (0.01=1% 1=100%)]/«itemtypedadare/proprietà/valore/...»\n" +
				"TAKE[,probability (0.01=1% 1=100%)[,quantity[,searchinbank (true/false)[,itemtype]]]]/nomeitem\n" +
				"TAKEBYTYPE[,probability (0.01=1% 1=100%)[,quantity[,searchinbank (true/false)]]]/itemtype\n" +
				"GUMP,titolo,gumptype (*)[,gumpconstructor]/text  *(0=messaggio - 1=yes/no - 2=response - 3=questgump - 4=multiple response)\n" +
				"BROWSER/url\n" +
				"SENDMSG[,color]/text\n" +
				"SENDASCIIMSG[,color][,fontnumber]/text (no accent)\n" +
				"WAITUNTIL[,duration][,timeout][/condition][/spawngroup]\n" +
				"WHILE/condition/spawngroup\n" +
				"SPAWN[,spawnername],subgroup\n" +
				"IF/condition/thenspawn[/elsespawn]\n" +
				"DESPAWN[,spawnername],subgroup\n" +
				"SPAWN[,spawnername],subgroup\n" +
				"GOTO/subgroup\n" +
				"COMMAND/command string (pay attention please!)\n" +
				"MUSIC,musicname[,distance]\n" +
				"SOUND,soundnumber\n" +
				"MEFFECT,itemid[,speed][,x,y,z][,x2,y2,z2]\n" +
				"EFFECT,itemid,duration[,x,y,z] OR EFFECT,itemid,durata[,trigmob]\n" +
				"POISON,levelname[,distance][,onlyplayers (true/false)]\n" +
				"DAMAGE,damage,phys,fire,cold,pois,energy[,distance][,onlyplayers (true/false)]\n" +
				"RESURRECT[,distance][,pets (true/false)]\n" +
				"CAST,spellnumber[,argomenti] OR CAST,spellname[,argomenti]\n" +
				"BCAST[,color][,fontnumber]/text\n" +
				"BSOUND,soundnumber",
				false, true);
			AddButton(width - 30, 5, 0x15E1, 0x15E5, 200, GumpButtonType.Page, 2);
			AddLabel(width - 38, 2, 0x384, "1");
			AddButton(width - 60, 5, 0x15E3, 0x15E7, 200, GumpButtonType.Page, 4);

			AddPage(2);
			AddLabel(27, 2, 0x384, "Value and Itemtype Keywords NB: ( « EQUALE TO < ) ( » EQUALS TO > )");
			AddHtml(25, 20, width - 10, 440,
				"property/@value\n\n" +
				"Special KeyWords\n" +
				"ARMOR,minlevel,maxlevel\n" +
				"WEAPON,minlevel,maxlevel\n" +
				"JARMOR,minlevel,maxlevel\n" +
				"JWEAPON,minlevel,maxlevel\n" +
				"SARMOR,minlevel,maxlevel\n" +
				"SHIELD,minlevel,maxlevel\n" +
				"JEWELRY,minlevel,maxlevel\n" +
				"ITEM,serial (to find that item)\n" +
				"SCROLL,mincircle,maxcircle\n" +
				"LOOT,methodname\n" +
				"NECROSCROLL,index\n" +
				"LOOTPACK,loottype\n" +
				"POTION\n" +
				"TAKEN (it will help you find item taken during quest, xmlspawners and such)\n" +
				"GIVEN (equal to TAKEN, but for item given)\n" +
				"MULTIADDON,filename\n\n" +
				"{the GET or RND keyword are used inside brachets}\n" +
				"RND,min,max\n" +
				"RNDBOOL\n" +
				"RNDLIST,int1[,int2,...]\n" +
				"RNDSTRLIST,str1[,str2,...]\n" +
				"MY,prop (for spawner mob)\n" +
				"GET,[itemname or serial or SETITEM(*)][,itemtype],prop (*: the setitem of spawner, it's written SETITEM, upper char!)\n" +
				"GET,[itemname or serial or SETITEM][,itemtype],«ATTACHMENT,type,name,property»\n" +
				"GETVAR,varname (XmlLocalVariable)\n" +
				"GETONMOB,mobname[,mobtype],prop\n" +
				"GETONMOB,mobname[,mobtype],«ATTACHMENT,type,nome,prop»\n" +
				"GETONCARRIED,itemname[,itemtype][,equippedonly (true/false)],prop\n" +
				"GETONCARRIED,itemname[,itemtype][,equippedonly (true/false)],«ATTACHMENT,type,nome,prop»\n" +
				"GETONTRIGMOB,prop\n" +
				"GETONTRIGMOB,«ATTACHMENT,type,name,prop»\n" +
				"GETONNEARBY,distance,name[,type][,searchcontainers (true/false)],prop\n" +
				"GETONNEARBY,distance,name[,type][,searchcontainers (true/false)],«ATTACHMENT,type,name,prop»\n" +
				"GETONPARENT,prop\n" +
				"GETONPARENT,«ATTACHMENT,type,name,prop»\n" +
				"GETONTHIS,prop\n" +
				"GETONTHIS,«ATTACHMENT,type,name,prop»\n" +
				"GETONTAKEN,prop\n" +
				"GETONTAKEN,«ATTACHMENT,type,name,prop»\n" +
				"GETONGIVEN,prop\n" +
				"GETONGIVEN,«ATTACHMENT,type,name,prop»\n" +
				"GETONSPAWN[,nomespawner],subgroup,prop\n" +
				"GETONSPAWN[,nomespawner],subgroup,COUNT (special - returns the number of objects spawned!)\n" +
				"GETONSPAWN[,spawnername],subgroup,«ATTACHMENT,type,name,prop»\n" +
				"GETFROMFILE,filename\n" +
				"GETACCOUNTTAG,tagname\n" +
				"MUL,value or MUL,min,max (fractal numbers)\n" +
				"INC,value or INC,min,max (integer numbers)\n" +
				"MOB,name[,mobtype] (search the id of a mob by name and eventually by type)\n" +
				"TRIGMOB\n" +
				"AMOUNTCARRIED,itemtype[,searchinbank (true/false)][,itemname]\n" +
				"PLAYERSINRANGE,distance\n" +
				"TRIGSKILL,name or value or base or cap\n" +
				"RANDNAME,nametype (female,male etc)\n" +
				"MUSIC,musicname[,distance]\n" +
				"SOUND,value\n" +
				"EFFECT,itemid,duration[,x,y,z]\n" +
				"BOLTEFFECT[,sound[,color]]\n" +
				"MEFFECT,itemid[,speed][,x,y,z][,x2,y2,z2]\n" +
				"PEFFECT,itemid,speed,[x,y,z]\n" +
				"POISON,level[,distance][,onlyplayers (true/false)]\n" +
				"DAMAGE,damage,phys,fire,cold,pois,energy[,distance][,onlyplayers (true/false)]\n" +
				"ADD[,probability (0.01=1% 1=100%)]/itemtype[,args]\n" +
				"ADD[,probability (0.01=1% 1=100%)]/«itemtype[,args]/prop/value...»\n" +
				"EQUIP[,probability (0.01=1% 1=100%)]/itemtype[,args]\n" +
				"EQUIP[,probability (0.01=1% 1=100%)]/«itemtype[,args]/prop/value...»\n" +
				"DELETE\n" +
				"KILL\n" +
				"UNEQUIP,layer[,delete (true/false)]\n" +
				"ATTACH[,probability (0.01=1% 1=100%)]/attachmenttype[,args]\n" +
				"ATTACH[,probability (0.01=1% 1=100%)]/«attachmenttype[,args]/prop/value...»\n" +
				"MSG[,probability (0.01=1% 1=100%)]/text\n" +
				"ASCIIMSG[,probability (0.01=1% 1=100%)][,color][,fontnumber]/text\n" +
				"SENDMSG[,probability (0.01=1% 1=100%)][,color]/text\n" +
				"SENDASCIIMSG[,probability (0.01=1% 1=100%)][,color][,fontnumber]/text\n" +
				"SAY[,probability (0.01=1% 1=100%)]/text\n" +
				"SPEECH[,probability (0.01=1% 1=100%)][,keywordnumber]\n" +
				"OFFSET,x,y,[,z]\n" +
				"ANIMATE,action[,framecount][,repeatcount][,forward true/false][,repeat true/false][,delay]\n" +
				"FACETO,x,y (turns the mobile to face in a direction by the coords given)\n" +
				"SETVALUE,nomevar,valore,durata (XmlValue)\n" +
				"FLASH,number (1 fade black - 2 fade white - 3 light flash - 4 light to black flash - 5 black flash SOLO PG)\n" +
				"PRIVMSG[,probability (0.01=1% 1=100%)][,color]/text (shows a message only to that player)\n" +
				"BCAST[,color][,font]/message\n" +
				"always...«ATTACHMENT,type,name,prop» as a property on  GET\n" +
				"SKILL,skillname\n" +
				"STEALABLE,stealable (true/false)",
				false, true);
			AddButton(width - 30, 5, 0x15E1, 0x15E5, 200, GumpButtonType.Page, 3);
			AddLabel(width - 41, 2, 0x384, "2");
			AddButton(width - 60, 5, 0x15E3, 0x15E7, 200, GumpButtonType.Page, 1);

			AddPage(3);
			AddLabel(27, 2, 0x384, "[ Commands");

			AddHtml(25, 20, width - 10, 440,
				"XmlAdd [-defaults]\n" +
				"XmlShow\n" +
				"XmlHide\n" +
				"XmlFind\n" +
				"AddAtt type [args]\n" +
				"GetAtt [type]\n" +
				"DelAtt [type][serialno]\n" +
				"AvailAtt\n" +
				"SmartStat [accesslevel GameMaster]\n" +
				"OptimalSmartSpawning [maxdiff]\n" +
				"XmlSpawnerWipe [prefix]\n" +
				"XmlSpawnerWipeAll [prefix]\n" +
				"XmlSpawnerRespawn [prefix]\n" +
				"XmlSpawnerRespawnAll [prefix]\n" +
				"XmlHome [go][gump][send]\n" +
				"XmlUnLoad filename [prefix]\n" +
				"XmlLoad filename [prefix]\n" +
				"XmlLoadHere filename [prefix][-maxrange range]\n" +
				"XmlNewLoad filename [prefix]\n" +
				"XmlNewLoadHere filename [prefix][-maxrange range]\n" +
				"XmlSave filename [prefix]\n" +
				"XmlSaveAll filename [prefix]\n" +
				"XmlSaveOld filename [prefix]\n" +
				"XmlSpawnerSaveAll filename [prefix]\n" +
				"XmlImportSpawners filename\n" +
				"XmlImportMap filename\n" +
				"XmlImportMSF filename\n" +
				"XmlDefaults [defaultpropertyname value]\n" +
				"XmlGet property\n" +
				"XmlSet property value",
				false, true);

			AddButton(width - 30, 5, 0x15E1, 0x15E5, 200, GumpButtonType.Page, 4);
			AddLabel(width - 41, 2, 0x384, "3");
			AddButton(width - 60, 5, 0x15E3, 0x15E7, 200, GumpButtonType.Page, 2);

			AddPage(4);
			AddLabel(27, 2, 0x384, "Quest types");
			AddHtml(25, 20, width - 10, 180,
				"KILL,mobtype[,count][,proptest]\n" +
				"KILLNAMED,mobname[,type][,count][,proptest]\n" +
				"GIVE,mobname,itemtype[,count][,proptest]\n" +
				"GIVENAMED,mobname,itemname[,type][,count][,proptest]\n" +
				"COLLECT,itemtype[,count][,proptest]\n" +
				"COLLECTNAMED,itemname[,itemtype][,count][,proptest]\n" +
				"ESCORT[,mobname][,proptest]\n",
				false, true);

			AddLabel(27, 200, 0x384, "Trigger/NoTriggerOnCarried");
			AddHtml(25, 220, width - 10, 50,
				"ATTACHMENT,name,type\n" +
				"itemname[,type][,EQUIPPED][,objective#,objective#,...]\n",
				false, true);

			AddLabel(27, 300, 0x384, "GUMPITEMS");
			AddHtml(25, 320, width - 10, 150,
				"BUTTON,gumpid,x,y\n" +
				"HTML,x,y,width,height,text\n" +
				"IMAGE,gumpid,x,y[,hue]\n" +
				"IMAGETILED,gumpid,x,y,width,height\n" +
				"ITEM,itemid,x,y[,hue]\n" +
				"LABEL,x,y,labelstring[,labelcolor]\n" +
				"RADIO,gumpid1,gumpid2,x,y[,initialstate]\n" +
				"TEXTENTRY,x,y,width,height[,text][,textcolor]\n",
				false, true);

			AddButton(width - 30, 5, 0x15E1, 0x15E5, 200, GumpButtonType.Page, 1);
			AddLabel(width - 41, 2, 0x384, "4");
			AddButton(width - 60, 5, 0x15E3, 0x15E7, 200, GumpButtonType.Page, 3);
		}
	}
	public class TextEntryGump : Gump
	{
		private XmlSpawner m_Spawner;
		private int m_index;
		private XmlSpawnerGump m_SpawnerGump;

		public TextEntryGump(XmlSpawner spawner, XmlSpawnerGump spawnergump, int index, int X, int Y)
			: base(X, Y)
		{
			if (spawner == null || spawner.Deleted)
				return;
			m_Spawner = spawner;
			m_index = index;
			m_SpawnerGump = spawnergump;

			AddPage(0);

			AddBackground(20, 0, 220, 354, 5054);
			AddAlphaRegion(20, 0, 220, 354);
			AddImageTiled(23, 5, 214, 270, 0x52);
			AddImageTiled(24, 6, 213, 261, 0xBBC);

			string label = spawner.Name + " entry " + index;
			AddLabel(28, 10, 0x384, label);

			// OK button
			AddButton(25, 325, 0xFB7, 0xFB9, 1, GumpButtonType.Reply, 0);
			// Close button
			AddButton(205, 325, 0xFB1, 0xFB3, 0, GumpButtonType.Reply, 0);
			// Edit button
			AddButton(100, 325, 0xEF, 0xEE, 2, GumpButtonType.Reply, 0);
			string str = null;
			if (index < m_Spawner.SpawnObjects.Length)
			{
				str = (string)m_Spawner.SpawnObjects[index].TypeName;
			}
			// main text entry area
			AddTextEntry(35, 30, 200, 251, 0, 0, str);

			// editing text entry areas
			// background for text entry area
			AddImageTiled(23, 275, 214, 23, 0x52);
			AddImageTiled(24, 276, 213, 21, 0xBBC);
			AddImageTiled(23, 300, 214, 23, 0x52);
			AddImageTiled(24, 301, 213, 21, 0xBBC);

			AddTextEntry(35, 275, 200, 21, 0, 1, null);
			AddTextEntry(35, 300, 200, 21, 0, 2, null);
		}

		public override void OnResponse(NetState state, RelayInfo info)
		{
			if (info == null || state == null || state.Mobile == null) return;

			if (m_Spawner == null || m_Spawner.Deleted)
				return;
			bool update_entry = false;
			bool edit_entry = false;
			switch (info.ButtonID)
			{
				case 0: // Close
					{
						update_entry = false;
						break;
					}
				case 1: // Okay
					{
						update_entry = true;
						break;
					}
				case 2: // Edit
					{
						edit_entry = true;
						break;
					}
				default:
					update_entry = true;
					break;
			}
			if (edit_entry)
			{
				// get the old text
				TextRelay entry = info.GetTextEntry(1);
				string oldtext = entry.Text;
				// get the new text
				entry = info.GetTextEntry(2);
				string newtext = entry.Text;
				// make the substitution
				entry = info.GetTextEntry(0);
				string origtext = entry.Text;
				if (origtext != null && oldtext != null && newtext != null)
				{
					try
					{
						int firstindex = origtext.IndexOf(oldtext);
						if (firstindex >= 0)
						{


							int secondindex = firstindex + oldtext.Length;

							int lastindex = origtext.Length - 1;

							string editedtext;
							if (firstindex > 0)
							{
								editedtext = origtext.Substring(0, firstindex) + newtext + origtext.Substring(secondindex, lastindex - secondindex + 1);
							}
							else
							{
								editedtext = newtext + origtext.Substring(secondindex, lastindex - secondindex + 1);
							}

							if (m_index < m_Spawner.SpawnObjects.Length)
							{
								m_Spawner.SpawnObjects[m_index].TypeName = editedtext;
							}
							else
							{
								// Update the creature list
								m_Spawner.SpawnObjects = m_SpawnerGump.CreateArray(info, state.Mobile);
							}
						}
					}
					catch { }

				}
				// open a new text entry gump
				state.Mobile.SendGump(new TextEntryGump(m_Spawner, m_SpawnerGump, m_index, this.X, this.Y));
				return;
			}
			if (update_entry)
			{
				TextRelay entry = info.GetTextEntry(0);
				if (m_index < m_Spawner.SpawnObjects.Length)
				{
					m_Spawner.SpawnObjects[m_index].TypeName = entry.Text;
				}
				else
				{
					// Update the creature list
					m_Spawner.SpawnObjects = m_SpawnerGump.CreateArray(info, state.Mobile);
				}
			}
			// Create a new gump

			//m_Spawner.OnDoubleClick( state.Mobile);
			// open a new spawner gump
			state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, m_SpawnerGump.m_ShowGump, m_SpawnerGump.xoffset, m_SpawnerGump.page));
		}
	}



	public class XmlSpawnerGump : Gump
	{
		private static int nclicks = 0;
		public XmlSpawner m_Spawner;
		public const int MaxSpawnEntries = 60;
		private const int MaxEntriesPerPage = 15;
		public int m_ShowGump = 0;
		public bool AllowGumpUpdate = true;
		public int xoffset = 0;
		public int initial_maxcount;
		public int page;
		public ReplacementEntry Rentry;

		public class ReplacementEntry
		{
			public string Typename;
			public int Index;
			public int Color;

			public ReplacementEntry()
			{
			}
		}

		public XmlSpawnerGump(XmlSpawner spawner, int X, int Y, int extension, int textextension, int newpage)
			: this(spawner, X, Y, extension, textextension, newpage, null)
		{
		}

		public XmlSpawnerGump(XmlSpawner spawner, int X, int Y, int extension, int textextension, int newpage, ReplacementEntry rentry)
			: base(X, Y)
		{
			if (spawner == null || spawner.Deleted)
				return;

			m_Spawner = spawner;
			spawner.SpawnerGump = this;
			xoffset = textextension;
			initial_maxcount = spawner.MaxCount;
			page = newpage;
			Rentry = rentry;

			AddPage(0);

			// automatically change the gump depending on whether sequential spawning and/or subgroups are activated

			if (spawner.SequentialSpawn >= 0 || spawner.HasSubGroups() || spawner.HasIndividualSpawnTimes())
			{
				// show the fully extended gump with subgroups and reset timer info
				m_ShowGump = 2;
			}
			/*
		else
			if(spawner.HasSubGroups() || spawner.SequentialSpawn >= 0)
		{
			// show partially extended gump with subgroups
			m_ShowGump = 1;
		}
		*/

			if (extension > 0)
			{
				m_ShowGump = extension;
			}
			if (extension < 0)
			{
				m_ShowGump = 0;
			}

			// if the expanded gump toggle has been activated then override the auto settings.


			if (m_ShowGump > 1)
			{
				AddBackground(0, 0, 670 + xoffset + 30, 474, 5054);
				AddAlphaRegion(0, 0, 670 + xoffset + 30, 474);
			}
			else
				if (m_ShowGump > 0)
				{
					AddBackground(0, 0, 335 + xoffset, 474, 5054);
					AddAlphaRegion(0, 0, 335 + xoffset, 474);
				}
				else
				{
					AddBackground(0, 0, 305 + xoffset, 474, 5054);
					AddAlphaRegion(0, 0, 305 + xoffset, 474);
				}

			// spawner name area
			AddImageTiled(3, 5, 227, 23, 0x52);
			AddImageTiled(4, 6, 225, 21, 0xBBC);
			AddTextEntry(6, 5, 222, 21, 0, 999, spawner.Name); // changed from color 50

			AddButton(5, 450, 0xFAE, 0xFAF, 4, GumpButtonType.Reply, 0);
			AddLabel(38, 450, 0x384, "Goto");

			//AddButton( 5, 428, 0xFB7, 0xFB9, 1, GumpButtonType.Reply, 0 );
			AddButton(5, 428, 0xFAE, 0xFAF, 1, GumpButtonType.Reply, 0);
            AddLabel(38, 428, 0x384, "Help");

			AddButton(71, 428, 0xFB4, 0xFB6, 2, GumpButtonType.Reply, 0);
			AddLabel(104, 428, 0x384, "Bring Home");
			AddButton(71, 450, 0xFA8, 0xFAA, 3, GumpButtonType.Reply, 0);
			AddLabel(104, 450, 0x384, "Respawn");

			// Props button
			AddButton(168, 428, 0xFAB, 0xFAD, 9999, GumpButtonType.Reply, 0);
			AddLabel(201, 428, 0x384, "Props");

			// Sort button
			AddButton(168, 450, 0xFAB, 0xFAD, 702, GumpButtonType.Reply, 0);
			AddLabel(201, 450, 0x384, "Sort");

			// Reset button
			AddButton(71, 406, 0xFA2, 0xFA3, 701, GumpButtonType.Reply, 0);
			AddLabel(104, 406, 0x384, "Reset");

			// Refresh button
			AddButton(168, 406, 0xFBD, 0xFBE, 9998, GumpButtonType.Reply, 0);
			AddLabel(201, 406, 0x384, "Refresh");

			AddButton(280, 395, m_Spawner.DisableGlobalAutoReset ? 0xD3 : 0xD2,
								m_Spawner.DisableGlobalAutoReset ? 0xD2 : 0xD3, 9997, GumpButtonType.Reply, 0);
			AddLabel(263, 410, m_Spawner.DisableGlobalAutoReset ? 68 : 33, "Disable");
			AddLabel(247, 424, m_Spawner.DisableGlobalAutoReset ? 68 : 33, "TickReset");

			// add run status display
			if (m_Spawner.Running)
			{
				AddButton(5, 399, 0x2A4E, 0x2A3A, 700, GumpButtonType.Reply, 0);
				AddLabel(38, 406, 0x384, "On");
			}
			else
			{
				AddButton(5, 399, 0x2A62, 0x2A3A, 700, GumpButtonType.Reply, 0);
				AddLabel(38, 406, 0x384, "Off");
			}

			// Add sequential spawn state
			if (m_Spawner.SequentialSpawn >= 0)
			{
				AddLabel(15, 365, 33, String.Format("{0}", m_Spawner.SequentialSpawn));
			}

			// Add Current / Max count labels
			AddLabel(231 + xoffset, 9, 68, "Count");
			AddLabel(270 + xoffset, 9, 33, "Max");

			if (m_ShowGump > 0)
			{
				// Add subgroup label
				AddLabel(334 + xoffset, 9, 68, "Sub");
			}
			if (m_ShowGump > 1)
			{
				// Add entry field labels
				AddLabel(303 + xoffset, 9, 68, "Per");
				AddLabel(329 + xoffset + 30, 9, 68, "Reset");
				AddLabel(368 + xoffset + 30, 9, 68, "To");
				AddLabel(392 + xoffset + 30, 9, 68, "Kills");
				AddLabel(432 + xoffset + 30, 9, 68, "MinD");
				AddLabel(472 + xoffset + 30, 9, 68, "MaxD");
				AddLabel(515 + xoffset + 30, 9, 68, "Rng");
				AddLabel(545 + xoffset + 30, 9, 68, "RK");
				AddLabel(565 + xoffset + 30, 9, 68, "Clr");
				AddLabel(590 + xoffset + 30, 9, 68, "NextSpawn");
			}

			// add area for spawner max
			AddLabel(180 + xoffset, 365, 50, "Spawner");
			AddImageTiled(267 + xoffset, 365, 35, 23, 0x52);
			AddImageTiled(268 + xoffset, 365, 32, 21, 0xBBC);
			AddTextEntry(273 + xoffset, 365, 33, 33, 33, 300, m_Spawner.MaxCount.ToString());

			// add area for spawner count
			AddImageTiled(231 + xoffset, 365, 35, 23, 0x52);
			AddImageTiled(232 + xoffset, 365, 32, 21, 0xBBC);
			AddLabel(233 + xoffset, 365, 68, m_Spawner.CurrentCount.ToString());

			// add the status string
			AddTextEntry(38, 384, 235, 33, 33, 900, m_Spawner.status_str);
			// add the page buttons
			for (int i = 0; i < (int)(MaxSpawnEntries / MaxEntriesPerPage); i++)
			{
				//AddButton( 38+i*30, 365, 2206, 2206, 0, GumpButtonType.Page, 1+i );
				AddButton(38 + i * 25, 365, 0x8B1 + i, 0x8B1 + i, 4000 + i, GumpButtonType.Reply, 0);
			}

			// add gump extension button
			if (m_ShowGump > 1)
				AddButton(645 + xoffset + 30, 450, 0x15E3, 0x15E7, 200, GumpButtonType.Reply, 0);
			else
				if (m_ShowGump > 0)
					AddButton(315 + xoffset, 450, 0x15E1, 0x15E5, 200, GumpButtonType.Reply, 0);
				else
					AddButton(285 + xoffset, 450, 0x15E1, 0x15E5, 200, GumpButtonType.Reply, 0);

			// add the textentry extender button
			if (xoffset > 0)
			{
				AddButton(160, 365, 0x15E3, 0x15E7, 201, GumpButtonType.Reply, 0);
			}
			else
			{
				AddButton(160, 365, 0x15E1, 0x15E5, 201, GumpButtonType.Reply, 0);
			}


			for (int i = 0; i < MaxSpawnEntries; i++)
			{
				if (page != (int)(i / MaxEntriesPerPage)) continue;

				string str = String.Empty;
				int texthue = 0;
				int background = 0xBBC;

				if (i % MaxEntriesPerPage == 0)
				{
					//AddPage(page+1);
					// add highlighted page button
					AddImageTiled(35 + page * 25, 363, 25, 25, 0xBBC);
					AddImage(38 + page * 25, 365, 0x8B1 + page);
				}

				if (i < m_Spawner.SpawnObjects.Length)
				{
					// disable button

					if (m_Spawner.SpawnObjects[i].Disabled)
					{
						// change the background for the spawn text entry if disabled
						background = 0x23F4;
						AddButton(2, 22 * (i % MaxEntriesPerPage) + 34, 0x82C, 0x82C, 6000 + i, GumpButtonType.Reply, 0);
					}
					else
					{
						AddButton(2, 22 * (i % MaxEntriesPerPage) + 36, 0x837, 0x837, 6000 + i, GumpButtonType.Reply, 0);
					}


				}

				bool hasreplacement = false;

				// check for replacement entries
				if (Rentry != null && Rentry.Index == i)
				{
					hasreplacement = true;
					str = Rentry.Typename;
					background = Rentry.Color;
					// replacement is one time only.
					Rentry = null;

				}


				// increment/decrement buttons
				AddButton(15, 22 * (i % MaxEntriesPerPage) + 34, 0x15E0, 0x15E4, 6 + (i * 2), GumpButtonType.Reply, 0);
				AddButton(30, 22 * (i % MaxEntriesPerPage) + 34, 0x15E2, 0x15E6, 7 + (i * 2), GumpButtonType.Reply, 0);

				// categorization gump button
				AddButton(171 + xoffset - 18, 22 * (i % MaxEntriesPerPage) + 34, 0x15E1, 0x15E5, 5000 + i, GumpButtonType.Reply, 0);

				// goto spawn button
				AddButton(171 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 0xFAE, 0xFAF, 1300 + i, GumpButtonType.Reply, 0);

				// text entry gump button
				AddButton(200 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 0xFAB, 0xFAD, 800 + i, GumpButtonType.Reply, 0);

				// background for text entry area
				AddImageTiled(48, 22 * (i % MaxEntriesPerPage) + 30, 133 + xoffset - 25, 23, 0x52);
				AddImageTiled(49, 22 * (i % MaxEntriesPerPage) + 31, 131 + xoffset - 25, 21, background);

				// Add page number
				//AddLabel( 15, 365, 33, String.Format("{0}",(int)(i/MaxEntriesPerPage + 1)) );
				//AddButton( 38+page*25, 365, 0x8B1+i, 0x8B1+i, 0, GumpButtonType.Page, 1+i );



				if (i < m_Spawner.SpawnObjects.Length)
				{
					if (!hasreplacement)
					{
						str = (string)m_Spawner.SpawnObjects[i].TypeName;
					}

					int count = m_Spawner.SpawnObjects[i].SpawnedObjects.Count;
					int max = m_Spawner.SpawnObjects[i].ActualMaxCount;
					int subgrp = m_Spawner.SpawnObjects[i].SubGroup;
					int spawnsper = m_Spawner.SpawnObjects[i].SpawnsPerTick;

					texthue = subgrp * 11;
					if (texthue < 0) texthue = 0;

					// Add current count
					AddImageTiled(231 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 35, 23, 0x52);
					AddImageTiled(232 + xoffset, 22 * (i % MaxEntriesPerPage) + 31, 32, 21, 0xBBC);
					AddLabel(233 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 68, count.ToString());

					// Add maximum count
					AddImageTiled(267 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 35, 23, 0x52);
					AddImageTiled(268 + xoffset, 22 * (i % MaxEntriesPerPage) + 31, 32, 21, 0xBBC);
					// AddTextEntry(x,y,w,ht,color,id,str)
					AddTextEntry(270 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 30, 30, 33, 500 + i, max.ToString());


					if (m_ShowGump > 0)
					{
						// Add subgroup
						AddImageTiled(334 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 25, 23, 0x52);
						AddImageTiled(335 + xoffset, 22 * (i % MaxEntriesPerPage) + 31, 22, 21, 0xBBC);
						AddTextEntry(338 + xoffset, 22 * (i % MaxEntriesPerPage) + 30, 17, 33, texthue, 600 + i, subgrp.ToString());
					}
					if (m_ShowGump > 1)
					{
						// Add subgroup timer fields

						string strrst = null;
						string strto = null;
						string strkill = null;
						string strmind = null;
						string strmaxd = null;
						string strnext = null;
						string strpackrange = null;
						string strspawnsper = spawnsper.ToString();

						if (m_Spawner.SpawnObjects[i].SequentialResetTime > 0 && m_Spawner.SpawnObjects[i].SubGroup > 0)
						{
							strrst = m_Spawner.SpawnObjects[i].SequentialResetTime.ToString();
							strto = m_Spawner.SpawnObjects[i].SequentialResetTo.ToString();
						}
						if (m_Spawner.SpawnObjects[i].KillsNeeded > 0)
						{
							strkill = m_Spawner.SpawnObjects[i].KillsNeeded.ToString();
						}

						if (m_Spawner.SpawnObjects[i].MinDelay >= 0)
						{
							strmind = m_Spawner.SpawnObjects[i].MinDelay.ToString();
						}

						if (m_Spawner.SpawnObjects[i].MaxDelay >= 0)
						{
							strmaxd = m_Spawner.SpawnObjects[i].MaxDelay.ToString();
						}

						if (m_Spawner.SpawnObjects[i].PackRange >= 0)
						{
							strpackrange = m_Spawner.SpawnObjects[i].PackRange.ToString();
						}

						if (m_Spawner.SpawnObjects[i].NextSpawn > DateTime.Now)
						{
							// if the next spawn tick of the spawner will occur after the subgroup is available for spawning
							// then report the next spawn tick since that is the earliest that the subgroup can actually be spawned
							if ((DateTime.Now + m_Spawner.NextSpawn) > m_Spawner.SpawnObjects[i].NextSpawn)
							{
								strnext = m_Spawner.NextSpawn.ToString();
							}
							else
							{
								// estimate the earliest the next spawn could occur as the first spawn tick after reaching the subgroup nextspawn 
								strnext = (m_Spawner.SpawnObjects[i].NextSpawn - DateTime.Now + m_Spawner.NextSpawn).ToString();
							}
						}
						else
						{
							strnext = m_Spawner.NextSpawn.ToString();
						}

						int yoff = 22 * (i % MaxEntriesPerPage) + 30;

						// spawns per tick
						AddImageTiled(303 + xoffset, yoff, 30, 23, 0x52);
						AddImageTiled(304 + xoffset, yoff + 1, 27, 21, 0xBBC);
						AddTextEntry(307 + xoffset, yoff, 22, 33, texthue, 1500 + i, strspawnsper);
						// reset time
						AddImageTiled(329 + xoffset + 30, yoff, 35, 23, 0x52);
						AddImageTiled(330 + xoffset + 30, yoff + 1, 32, 21, 0xBBC);
						AddTextEntry(333 + xoffset + 30, yoff, 27, 33, texthue, 1000 + i, strrst);
						// reset to
						AddImageTiled(365 + xoffset + 30, yoff, 26, 23, 0x52);
						AddImageTiled(366 + xoffset + 30, yoff + 1, 23, 21, 0xBBC);
						AddTextEntry(369 + xoffset + 30, yoff, 18, 33, texthue, 1100 + i, strto);
						// kills needed
						AddImageTiled(392 + xoffset + 30, yoff, 35, 23, 0x52);
						AddImageTiled(393 + xoffset + 30, yoff + 1, 32, 21, 0xBBC);
						AddTextEntry(396 + xoffset + 30, yoff, 27, 33, texthue, 1200 + i, strkill);

						// mindelay
						AddImageTiled(428 + xoffset + 30, yoff, 41, 23, 0x52);
						AddImageTiled(429 + xoffset + 30, yoff + 1, 38, 21, 0xBBC);
						AddTextEntry(432 + xoffset + 30, yoff, 33, 33, texthue, 1300 + i, strmind);

						// maxdelay
						AddImageTiled(470 + xoffset + 30, yoff, 41, 23, 0x52);
						AddImageTiled(471 + xoffset + 30, yoff + 1, 38, 21, 0xBBC);
						AddTextEntry(474 + xoffset + 30, yoff, 33, 33, texthue, 1400 + i, strmaxd);

						// packrange
						AddImageTiled(512 + xoffset + 30, yoff, 33, 23, 0x52);
						AddImageTiled(513 + xoffset + 30, yoff + 1, 30, 21, 0xBBC);
						AddTextEntry(516 + xoffset + 30, yoff, 25, 33, texthue, 1600 + i, strpackrange);

						if (m_Spawner.SequentialSpawn >= 0)
						{
							// restrict kills button
							AddButton(545 + xoffset + 30, yoff, m_Spawner.SpawnObjects[i].RestrictKillsToSubgroup ? 0xD3 : 0xD2,
								m_Spawner.SpawnObjects[i].RestrictKillsToSubgroup ? 0xD2 : 0xD3, 300 + i, GumpButtonType.Reply, 0);

							//clear on advance button for spawn entries in subgroups that require kills
							AddButton(565 + xoffset + 30, yoff, m_Spawner.SpawnObjects[i].ClearOnAdvance ? 0xD3 : 0xD2,
								m_Spawner.SpawnObjects[i].ClearOnAdvance ? 0xD2 : 0xD3, 400 + i, GumpButtonType.Reply, 0);
						}

						// add the next spawn time
						AddLabelCropped(590 + xoffset + 30, yoff, 70, 20, 55, strnext);

					}

					//AddButton( 20, 22 * (i%MaxEntriesPerPage) + 34, 0x15E3, 0x15E7, 5 + (i * 2), GumpButtonType.Reply, 0 );
				}
				// the spawn specification text
				//if(str != null)
				AddTextEntry(52, 22 * (i % MaxEntriesPerPage) + 31, 119 + xoffset - 25, 21, texthue, i, str);
			}
		}

		public XmlSpawner.SpawnObject[] CreateArray(RelayInfo info, Mobile from)
		{
			ArrayList SpawnObjects = new ArrayList();

			for (int i = 0; i < MaxSpawnEntries; i++)
			{
				TextRelay te = info.GetTextEntry(i);

				if (te != null)
				{
					string str = te.Text;

					if (str.Length > 0)
					{
						str = str.Trim();
#if(BOOKTEXTENTRY)
						if (i < m_Spawner.SpawnObjects.Length)
						{
							string currenttext = m_Spawner.SpawnObjects[i].TypeName;
							if (currenttext != null && currenttext.Length >= 230)
							{
								str = currenttext;
							}
						}
#endif
						string typestr = BaseXmlSpawner.ParseObjectType(str);

						Type type = SpawnerType.GetType(typestr);

						if (type != null)
							SpawnObjects.Add(new XmlSpawner.SpawnObject(from, m_Spawner, str, 0));
						else
						{
							// check for special keywords
							if (typestr != null && (BaseXmlSpawner.IsTypeOrItemKeyword(typestr) || typestr.IndexOf("{") != -1 || typestr.StartsWith("*") || typestr.StartsWith("#")))
							{
								SpawnObjects.Add(new XmlSpawner.SpawnObject(from, m_Spawner, str, 0));
							}
							else
								m_Spawner.status_str = String.Format("{0} is not a valid type name.", str);
							//from.SendMessage( "{0} is not a valid type name.", str );
						}

					}
				}
			}

			return (XmlSpawner.SpawnObject[])SpawnObjects.ToArray(typeof(XmlSpawner.SpawnObject));
		}

		public void UpdateTypeNames(Mobile from, RelayInfo info)
		{
			for (int i = 0; i < MaxSpawnEntries; i++)
			{
				TextRelay te = info.GetTextEntry(i);

				if (te != null)
				{
					string str = te.Text;

					if (str.Length > 0)
					{
						str = str.Trim();
						if (i < m_Spawner.SpawnObjects.Length)
						{
							// check to see if the existing typename is longer than the max textentry buffer
							// if it is then dont update it since we will assume that the textentry has truncated the actual string
							// that could be longer than the buffer if booktextentry is used

#if(BOOKTEXTENTRY)
							string currentstr = m_Spawner.SpawnObjects[i].TypeName;
							if (currentstr != null && currentstr.Length < 230)
#endif
							{
								if (m_Spawner.SpawnObjects[i].TypeName != str)
								{
									CommandLogging.WriteLine(from, "{0} {1} changed XmlSpawner {2} '{3}' [{4}, {5}] ({6}) : {7} to {8}", from.AccessLevel, CommandLogging.Format(from), m_Spawner.Serial, m_Spawner.Name, m_Spawner.GetWorldLocation().X, m_Spawner.GetWorldLocation().Y, m_Spawner.Map, m_Spawner.SpawnObjects[i].TypeName, str);

								}

								m_Spawner.SpawnObjects[i].TypeName = str;
							}
						}

					}
				}
			}
		}


#if(BOOKTEXTENTRY)

		public static void ProcessSpawnerBookEntry(Mobile from, object[] args, string entry)
		{
			if (from == null || args == null || args.Length < 6) return;

			XmlSpawner m_Spawner = (XmlSpawner)args[0];
			int m_Index = (int)args[1];
			int m_X = (int)args[2];
			int m_Y = (int)args[3];
			int m_Extension = (int)args[4];
			int m_page = (int)args[5];
			if (m_Spawner == null || m_Spawner.SpawnObjects == null) return;

			// place the book text into the spawn entry
			if (m_Index < m_Spawner.SpawnObjects.Length)
			{
				XmlSpawner.SpawnObject so = m_Spawner.SpawnObjects[m_Index];

				if (so.TypeName != entry)
				{
					CommandLogging.WriteLine(from, "{0} {1} changed XmlSpawner {2} '{3}' [{4}, {5}] ({6}) : {7} to {8}", from.AccessLevel, CommandLogging.Format(from), m_Spawner.Serial, m_Spawner.Name, m_Spawner.GetWorldLocation().X, m_Spawner.GetWorldLocation().Y, m_Spawner.Map, so.TypeName, entry);

				}

				so.TypeName = entry;
			}
			else
			{
				
				// add a new spawn entry
				m_Spawner.m_SpawnObjects.Add(new XmlSpawner.SpawnObject(from, m_Spawner, entry, 1));

				m_Index = m_Spawner.SpawnObjects.Length - 1;

				// and bump the maxcount of the spawner
				m_Spawner.MaxCount++;
			}

			// refresh the spawner gumps			
			RefreshSpawnerGumps(from);

			// and refresh the current one
			//from.SendGump( new XmlSpawnerGump(m_Spawner, m_X, m_Y, m_Extension,0, m_page) );

			// return the text entry focus to the book.  Havent figured out how to do that yet.
		}


#endif

		public static void Refresh_Callback(object state)
		{
			object[] args = (object[])state;
			Mobile m = (Mobile)args[0];
			// refresh the spawner gumps			
			RefreshSpawnerGumps(m);
		}

		public static void RefreshSpawnerGumps(Mobile from)
		{
			if (from == null) return;

			NetState ns = from.NetState;

			if (ns != null && ns.Gumps != null)
			{

				ArrayList refresh = new ArrayList();

				foreach (Gump g in ns.Gumps)
				{

					if (g is XmlSpawnerGump)
					{
						XmlSpawnerGump xg = (XmlSpawnerGump)g;

						// clear the gump status on the spawner associated with the gump
						if (xg.m_Spawner != null)
						{
							// and add the old gump to the removal list
							refresh.Add(xg);
						}
					}
				}

				// close all of the currently opened spawner gumps
				from.CloseGump(typeof(XmlSpawnerGump));


				// reopen the closed gumps from the gump collection
				foreach (XmlSpawnerGump g in refresh)
				{
					// reopen a new gump for the spawner
					if (g.m_Spawner != null /*&& g.m_Spawner.SpawnerGump == g */)
					{
						// flag the current gump on the spawner as closed
						g.m_Spawner.GumpReset = true;

						XmlSpawnerGump xg = new XmlSpawnerGump(g.m_Spawner, g.X, g.Y, g.m_ShowGump, g.xoffset, g.page, g.Rentry);

						from.SendGump(xg);
					}
				}
			}
		}


		private bool ValidGotoObject(Mobile from, object o)
		{
			if (o is Item)
			{
				Item i = o as Item;
				if (!i.Deleted && (i.Map != null) && (i.Map != Map.Internal))
					return true;

				if (from != null && !from.Deleted)
				{
					from.SendMessage("{0} is not available", i);
				}
			}
			else
				if (o is Mobile)
				{
					Mobile m = o as Mobile;
					if (!m.Deleted && (m.Map != null) && (m.Map != Map.Internal))
						return true;

					if (from != null && !from.Deleted)
					{
						from.SendMessage("{0} is not available", m);
					}
				}

			return false;
		}

		public override void OnResponse(NetState state, RelayInfo info)
		{
			if (m_Spawner == null || m_Spawner.Deleted || state == null || info == null)
			{
				if (m_Spawner != null) m_Spawner.SpawnerGump = null;
				return;
			}

			// restrict access to the original creator or someone of higher access level
			//if (m_Spawner.FirstModifiedBy != null && m_Spawner.FirstModifiedBy != state.Mobile && state.Mobile.AccessLevel <= m_Spawner.FirstModifiedBy.AccessLevel)
			//return;


			// Get the current name
			TextRelay tr = info.GetTextEntry(999);
			if (tr != null)
			{
				m_Spawner.Name = tr.Text;
			}

			// update typenames of the spawn objects based upon the current text entry strings
			UpdateTypeNames(state.Mobile, info);

			// Update the creature list
			m_Spawner.SpawnObjects = CreateArray(info, state.Mobile);

			if (m_Spawner.SpawnObjects == null)
			{
				m_Spawner.SpawnerGump = null;
				return;
			}

			AllowGumpUpdate = true;

			for (int i = 0; i < m_Spawner.SpawnObjects.Length; i++)
			{
				if (page != (int)(i / MaxEntriesPerPage)) continue;

				// check the max count entry
				TextRelay temcnt = info.GetTextEntry(500 + i);
				if (temcnt != null)
				{
					int maxval = 0;
					try { maxval = Convert.ToInt32(temcnt.Text, 10); }
					catch { }
					if (maxval < 0) maxval = 0;

					m_Spawner.SpawnObjects[i].MaxCount = maxval;
				}

				if (m_ShowGump > 0)
				{
					// check the subgroup entry
					TextRelay tegrp = info.GetTextEntry(600 + i);
					if (tegrp != null)
					{
						int grpval = 0;
						try { grpval = Convert.ToInt32(tegrp.Text, 10); }
						catch { }
						if (grpval < 0) grpval = 0;

						m_Spawner.SpawnObjects[i].SubGroup = grpval;
					}
				}

				if (m_ShowGump > 1)
				{
					// note, while these values can be entered in any spawn entry, they will only be assigned to the subgroup leader
					int subgroupindex = m_Spawner.GetCurrentSequentialSpawnIndex(m_Spawner.SpawnObjects[i].SubGroup);
					TextRelay tegrp;

					if (subgroupindex >= 0 && subgroupindex < m_Spawner.SpawnObjects.Length)
					{
						// check the *reset time* entry
						tegrp = info.GetTextEntry(1000 + i);
						if (tegrp != null && tegrp.Text != null && tegrp.Text.Length > 0)
						{
							double grpval = 0;
							try { grpval = Convert.ToDouble(tegrp.Text); }
							catch { }
							if (grpval < 0) grpval = 0;

							m_Spawner.SpawnObjects[i].SequentialResetTime = 0;

							m_Spawner.SpawnObjects[subgroupindex].SequentialResetTime = grpval;
						}
						// check the *reset to* entry
						tegrp = info.GetTextEntry(1100 + i);
						if (tegrp != null && tegrp.Text != null && tegrp.Text.Length > 0)
						{
							int grpval = 0;
							try { grpval = Convert.ToInt32(tegrp.Text, 10); }
							catch { }
							if (grpval < 0) grpval = 0;

							m_Spawner.SpawnObjects[subgroupindex].SequentialResetTo = grpval;
						}
						// check the kills entry
						tegrp = info.GetTextEntry(1200 + i);
						if (tegrp != null && tegrp.Text != null && tegrp.Text.Length > 0)
						{
							int grpval = 0;
							try { grpval = Convert.ToInt32(tegrp.Text, 10); }
							catch { }
							if (grpval < 0) grpval = 0;

							m_Spawner.SpawnObjects[subgroupindex].KillsNeeded = grpval;
						}

					}

					// check the mindelay
					tegrp = info.GetTextEntry(1300 + i);
					if (tegrp != null)
					{
						if (tegrp.Text != null && tegrp.Text.Length > 0)
						{
							double grpval = -1;
							try { grpval = Convert.ToDouble(tegrp.Text); }
							catch { }
							if (grpval < 0) grpval = -1;

							// if this value has changed, then update the next spawn time
							if (grpval != m_Spawner.SpawnObjects[i].MinDelay)
							{
								m_Spawner.SpawnObjects[i].MinDelay = grpval;
								m_Spawner.RefreshNextSpawnTime(m_Spawner.SpawnObjects[i]);
							}
						}
						else
						{
							m_Spawner.SpawnObjects[i].MinDelay = -1;
							m_Spawner.SpawnObjects[i].MaxDelay = -1;
							m_Spawner.RefreshNextSpawnTime(m_Spawner.SpawnObjects[i]);
						}
					}

					// check the maxdelay
					tegrp = info.GetTextEntry(1400 + i);
					if (tegrp != null)
					{
						if (tegrp.Text != null && tegrp.Text.Length > 0)
						{
							double grpval = -1;
							try { grpval = Convert.ToDouble(tegrp.Text); }
							catch { }
							if (grpval < 0) grpval = -1;

							// if this value has changed, then update the next spawn time
							if (grpval != m_Spawner.SpawnObjects[i].MaxDelay)
							{
								m_Spawner.SpawnObjects[i].MaxDelay = grpval;
								m_Spawner.RefreshNextSpawnTime(m_Spawner.SpawnObjects[i]);
							}
						}
						else
						{
							m_Spawner.SpawnObjects[i].MinDelay = -1;
							m_Spawner.SpawnObjects[i].MaxDelay = -1;
							m_Spawner.RefreshNextSpawnTime(m_Spawner.SpawnObjects[i]);
						}
					}

					// check the spawns per tick
					tegrp = info.GetTextEntry(1500 + i);
					if (tegrp != null)
					{
						if (tegrp.Text != null && tegrp.Text.Length > 0)
						{
							int grpval = 1;
							try { grpval = int.Parse(tegrp.Text); }
							catch { }
							if (grpval < 0) grpval = 1;

							// if this value has changed, then update the next spawn time
							if (grpval != m_Spawner.SpawnObjects[i].SpawnsPerTick)
							{
								m_Spawner.SpawnObjects[i].SpawnsPerTick = grpval;
							}
						}
						else
						{
							m_Spawner.SpawnObjects[i].SpawnsPerTick = 1;
						}
					}

					// check the packrange
					tegrp = info.GetTextEntry(1600 + i);
					if (tegrp != null)
					{
						if (tegrp.Text != null && tegrp.Text.Length > 0)
						{
							int grpval = 1;
							try { grpval = int.Parse(tegrp.Text); }
							catch { }
							if (grpval < 0) grpval = 1;

							// if this value has changed, then update 
							if (grpval != m_Spawner.SpawnObjects[i].PackRange)
							{
								m_Spawner.SpawnObjects[i].PackRange = grpval;
							}
						}
						else
						{
							m_Spawner.SpawnObjects[i].PackRange = -1;
						}
					}
				}
			}

			// Update the maxcount
			TextRelay temax = info.GetTextEntry(300);
			if (temax != null)
			{
				int maxval = 0;
				try { maxval = Convert.ToInt32(temax.Text, 10); }
				catch { }
				if (maxval < 0) maxval = 0;
				// if the maxcount of the spawner has been altered external to the interface (e.g. via props, or by the running spawner itself
				// then that change will override the text entry
				if (m_Spawner.MaxCount == this.initial_maxcount)
				{
					m_Spawner.MaxCount = maxval;
				}
			}

			switch (info.ButtonID)
			{
				case 0: // Close
					{
						// clear any text entry books
						m_Spawner.DeleteTextEntryBook();
						// and reset the gump status
						m_Spawner.GumpReset = true;

						return;
					}
				case 1: // Help
					{
						//state.Mobile.SendGump( new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump));
						state.Mobile.SendGump(new HelpGump(m_Spawner, this, this.X + 290, this.Y));
						break;
					}
				case 2: // Bring everything home
					{
						m_Spawner.BringToHome();
						break;
					}
				case 3: // Complete respawn
					{
						m_Spawner.Respawn();
						//m_Spawner.AdvanceSequential();
						m_Spawner.m_killcount = 0;
						break;
					}
				case 4: // Goto
					{
						state.Mobile.Location = m_Spawner.Location;
						state.Mobile.Map = m_Spawner.Map;
						break;
					}
				case 200: // gump extension
					{
						if (this.m_ShowGump > 1)
							state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, -1, this.xoffset, this.page));
						else
							state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump + 2, this.xoffset, this.page));
						return;
					}
				case 201: // gump text extension
					{
						if (this.xoffset > 0)
							state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, 0, this.page));
						else
							state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, 250, this.page));
						return;
					}
				case 700: // Start/stop spawner
					{
						if (m_Spawner.Running)
							m_Spawner.Running = false;
						else
							m_Spawner.Running = true;
						break;
					}
				case 701: // Complete reset
					{
						m_Spawner.Reset();
						break;
					}
				case 702: // Sort spawns
					{
						m_Spawner.SortSpawns();
						break;
					}
				case 900: // empty the status string
					{
						m_Spawner.status_str = "";
						break;
					}
				case 9997:
					{
						m_Spawner.DisableGlobalAutoReset=!m_Spawner.DisableGlobalAutoReset;
						break;
					}
				case 9998:  // refresh the gump
					{
						state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, this.xoffset, this.page));
						return;
					}
				case 9999:
					{
						// Show the props window for the spawner, as well as a new gump
						state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, this.xoffset, this.page));
#if(NEWPROPSGUMP)
						state.Mobile.SendGump(new XmlPropertiesGump(state.Mobile, m_Spawner));
#else
					state.Mobile.SendGump( new PropertiesGump( state.Mobile, m_Spawner ) );
#endif
						return;
					}
				default:
					{
						// check the restrict kills flag
						if (info.ButtonID >= 300 && info.ButtonID < 300 + MaxSpawnEntries)
						{
							int index = info.ButtonID - 300;
							if (index < m_Spawner.SpawnObjects.Length)
								m_Spawner.SpawnObjects[index].RestrictKillsToSubgroup = !m_Spawner.SpawnObjects[index].RestrictKillsToSubgroup;

						}
						else
							// check the clear on advance flag
							if (info.ButtonID >= 400 && info.ButtonID < 400 + MaxSpawnEntries)
							{
								int index = info.ButtonID - 400;
								if (index < m_Spawner.SpawnObjects.Length)
									m_Spawner.SpawnObjects[index].ClearOnAdvance = !m_Spawner.SpawnObjects[index].ClearOnAdvance;
							}
							else
								// text entry gump scroll buttons
								if (info.ButtonID >= 800 && info.ButtonID < 800 + MaxSpawnEntries)
								{
									// open the text entry gump
									int index = info.ButtonID - 800;
									// open a text entry gump
#if(BOOKTEXTENTRY)
									// display a new gump
									XmlSpawnerGump newgump = new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, this.xoffset, this.page);
									state.Mobile.SendGump(newgump);

									// is there an existing book associated with the gump?
									if (m_Spawner.m_TextEntryBook == null)
									{
										m_Spawner.m_TextEntryBook = new List<XmlTextEntryBook>();
									}

									object[] args = new object[6];

									args[0] = m_Spawner;
									args[1] = index;
									args[2] = X;
									args[3] = Y;
									args[4] = m_ShowGump;
									args[5] = page;

									XmlTextEntryBook book = new XmlTextEntryBook(0, String.Empty, m_Spawner.Name, 20, true, new XmlTextEntryBookCallback(ProcessSpawnerBookEntry), args);

									m_Spawner.m_TextEntryBook.Add(book);

									book.Title = String.Format("Entry {0}", index);
									book.Author = m_Spawner.Name;

									// fill the contents of the book with the current text entry data
									string text = String.Empty;
									if (m_Spawner.SpawnObjects != null && index < m_Spawner.SpawnObjects.Length)
									{
										text = m_Spawner.SpawnObjects[index].TypeName;
									}
									book.FillTextEntryBook(text);

									// put the book at the location of the player so that it can be opened, but drop it below visible range
									book.Visible = false;
									book.Movable = false;
									book.MoveToWorld(new Point3D(state.Mobile.Location.X, state.Mobile.Location.Y, state.Mobile.Location.Z - 100), state.Mobile.Map);

									// and open it
									book.OnDoubleClick(state.Mobile);


#else
							state.Mobile.SendGump( new TextEntryGump(m_Spawner,this, index, this.X, this.Y));
#endif
									return;
								}
								else
									// goto spawn buttons
									if (info.ButtonID >= 1300 && info.ButtonID < 1300 + MaxSpawnEntries)
									{
										nclicks++;
										// find the location of the spawn at the specified index
										int index = info.ButtonID - 1300;
										if (index < m_Spawner.SpawnObjects.Length)
										{
											int scount = m_Spawner.SpawnObjects[index].SpawnedObjects.Count;
											if (scount > 0)
											{
												object so = m_Spawner.SpawnObjects[index].SpawnedObjects[nclicks % scount];
												if (ValidGotoObject(state.Mobile, so))
												{
													IPoint3D o = so as IPoint3D;
													if (o != null)
													{
														Map m = m_Spawner.Map;
														if (o is Item)
															m = ((Item)o).Map;
														if (o is Mobile)
															m = ((Mobile)o).Map;

														state.Mobile.Location = new Point3D(o);
														state.Mobile.Map = m;
													}
												}
											}
										}
									}
									else
										// page buttons
										if (info.ButtonID >= 4000 && info.ButtonID < 4001 + (int)(MaxSpawnEntries / MaxEntriesPerPage))
										{
											// which page
											this.page = info.ButtonID - 4000;

										}
										else
											// toggle the disabled state of the entry
											if (info.ButtonID >= 6000 && info.ButtonID < 6000 + MaxSpawnEntries)
											{
												int index = info.ButtonID - 6000;

												if (index < m_Spawner.SpawnObjects.Length)
												{
													m_Spawner.SpawnObjects[index].Disabled = !m_Spawner.SpawnObjects[index].Disabled;

													// clear any current spawns on the disabled entry
													if (m_Spawner.SpawnObjects[index].Disabled)
														m_Spawner.RemoveSpawnObjects(m_Spawner.SpawnObjects[index]);
												}
											}
											else
												if (info.ButtonID >= 5000 && info.ButtonID < 5000 + MaxSpawnEntries)
												{
													int i = info.ButtonID - 5000;




													string categorystring = null;
													string entrystring = null;

													TextRelay te = info.GetTextEntry(i);

													if (te != null && te.Text != null)
													{
														// get the string

														string[] cargs = te.Text.Split(',');

														// parse out any comma separated args
														categorystring = cargs[0];

														entrystring = te.Text;
													}


													if (categorystring == null || categorystring.Length == 0)
													{

														XmlSpawnerGump newg = new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, this.xoffset, this.page);
														state.Mobile.SendGump(newg);

														// if no string has been entered then just use the full categorized add gump
														state.Mobile.CloseGump(typeof(Server.Gumps.XmlCategorizedAddGump));
														state.Mobile.SendGump(new Server.Gumps.XmlCategorizedAddGump(state.Mobile, i, newg));
													}
													else
													{
														// use the XmlPartialCategorizedAddGump
														state.Mobile.CloseGump(typeof(Server.Gumps.XmlPartialCategorizedAddGump));

														//Type [] types = (Type[])XmlPartialCategorizedAddGump.Match( categorystring ).ToArray( typeof( Type ) );
														ArrayList types = XmlPartialCategorizedAddGump.Match(categorystring);


														XmlSpawnerGump.ReplacementEntry re = new XmlSpawnerGump.ReplacementEntry();
														re.Typename = entrystring;
														re.Index = i;
														re.Color = 0x1436;

														XmlSpawnerGump newg = new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, this.xoffset, this.page, re);

														state.Mobile.SendGump(new XmlPartialCategorizedAddGump(state.Mobile, categorystring, 0, types, true, i, newg));

														state.Mobile.SendGump(newg);
													}

													return;


												}
												else
												{
													// up and down arrows
													int buttonID = info.ButtonID - 6;
													int index = buttonID / 2;
													int type = buttonID % 2;

													TextRelay entry = info.GetTextEntry(index);

													if (entry != null && entry.Text.Length > 0)
													{
														string entrystr = entry.Text;

#if(BOOKTEXTENTRY)
														if (index < m_Spawner.SpawnObjects.Length)
														{
															string str = m_Spawner.SpawnObjects[index].TypeName;

															if (str != null && str.Length >= 230)
																entrystr = str;
														}
#endif

														if (type == 0) // Add creature
														{
															m_Spawner.AddSpawnObject(entrystr);
														}
														else // Remove creatures
														{
															m_Spawner.DeleteSpawnObject(state.Mobile, entrystr);


														}
													}
												}
						break;
					}
			}
			// Create a new gump
			//m_Spawner.OnDoubleClick( state.Mobile);
			state.Mobile.SendGump(new XmlSpawnerGump(m_Spawner, this.X, this.Y, this.m_ShowGump, this.xoffset, this.page, this.Rentry));
		}
	}
}
